#include "include.h"

/*
 * pin and release - control swapping behaviour
 */

typedef struct page_pin_data {
    sos_pcb *pcb;
    seL4_Word proc_vaddr;
    seL4_Word sos_vaddr;
    page_pin_cb cb;
    void *cookie;
    int err;
} page_pin_data;

static void _frame_pin(int err, void *cookie);

static void _pin_reply(void *cookie) {
    page_pin_data *data = (page_pin_data *) cookie;
    (*(data->cb))(data->err, data->sos_vaddr, data->cookie);
    free(data);
    return;
}

int sos_page_pin(sos_pcb *pcb, seL4_Word proc_vaddr,
                 page_pin_cb cb, void *cookie) {
    int err;

    dprintf(5, "page_pin: PID %d pinning frame at %p\n", pcb->pid, proc_vaddr);

    page_pin_data *data = malloc(sizeof(page_pin_data));
    if (data == NULL) {
        // XXX Use scratch to call back and exit
        return SOS_PAGE_TABLE_OOM;
    }
    bzero((void *) data, sizeof(page_pin_data));

    // Copy args into heap
    data->pcb = pcb;
    data->proc_vaddr = proc_vaddr;
    data->cb = cb;
    data->cookie = cookie;

    // Run the fault handler - need to be sure we have a page.
    dprintf(6, "page_pin: faulting frame at %p\n", proc_vaddr);
    err = sos_page_fault_handle(pcb, proc_vaddr, &_frame_pin, (void *) data);

    return err;
}

// Fault handler has returned.
static void _frame_pin(int err, void *cookie) {
    page_pin_data *data = (page_pin_data *) cookie;
    page_table_entry *pte;

    dprintf(6, "page_pin_frame_pin: fault handler returned\n");

    // Check for fault handler errors
    if (err != SOS_PAGE_TABLE_SUCCESS) {
        dprintf(5, "page_pin: could not fault frame (%d)\n", err);
        data->err = err;
        _pin_reply((void *) data);
        return;
    }

    // Grab PTE, assert framenumber & not swapped, convert.
    err = page_walk_nofault(data->pcb, data->proc_vaddr, &pte);
    conditional_panic(err, "Unexpected fault - bad assumption?");

    // frame_pin - should succeed
    data->sos_vaddr = frame_translate_number(pte->framenumber);
    err = frame_pin(data->sos_vaddr);
    pte->attrs.pinned = 1;

    // call back
    data->err = err;
    _pin_reply((void *) data);
    return;
}

/* Synchronous release */
int sos_page_release(sos_pcb *pcb, seL4_Word proc_vaddr) {
    // Can do a no-fault walk, pull out the frame number, then call frame_release.
    int err;
    page_table_entry *pte;
    seL4_Word sos_vaddr;

    // Grab PTE, undo pinned bit
    err = page_walk_nofault(pcb, proc_vaddr, &pte);
    if (err) {
        dprintf(5, "page_release: pinned frame has disappeared?"
                   "PCB %p page %p\n", pcb, proc_vaddr);
        return err;
    }

    sos_vaddr = frame_translate_number(pte->framenumber);
    pte->attrs.pinned = 0;
    err = frame_release(sos_vaddr);

    return err;
}
